package org.aplikator.client.widgets;

import java.io.Serializable;

import org.aplikator.client.Aplikator;
import org.aplikator.client.Aplikator.AplikatorResources;
import org.aplikator.client.data.Record;
import org.aplikator.client.descriptor.ViewDTO;
import org.aplikator.client.descriptor.PropertyDTO;
import org.aplikator.client.panels.GlassPanel;
import org.aplikator.client.panels.HorizontalFlowPanel;
import org.aplikator.client.rpc.impl.GetSuggestions;
import org.aplikator.client.rpc.impl.GetSuggestionsResponse;

import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.FocusEvent;
import com.google.gwt.event.dom.client.FocusHandler;
import com.google.gwt.event.logical.shared.SelectionEvent;
import com.google.gwt.event.logical.shared.SelectionHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.resources.client.ClientBundle;
import com.google.gwt.resources.client.CssResource;
import com.google.gwt.resources.client.ImageResource;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.DialogBox;
import com.google.gwt.user.client.ui.DockLayoutPanel;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.SuggestBox;
import com.google.gwt.user.client.ui.SuggestOracle;
import com.google.gwt.user.client.ui.SuggestOracle.Suggestion;
import com.google.gwt.user.client.ui.Widget;

public class ReferenceFieldWidget extends ContainerFieldBase implements DataField<Integer> {

    public interface RefFormWidgetResources extends ClientBundle {
        public static final RefFormWidgetResources INSTANCE = GWT.create(RefFormWidgetResources.class);

        @Source("RefFormWidget.css")
        public RefFormWidgetCss css();

        @Source("images/icons/find16.png")
        ImageResource find();
    }

    public interface RefFormWidgetCss extends CssResource {
        public String refSearchLeft();

        public String refSearchRight();

        public String refSearchAll();
    }

    static {
        RefFormWidgetResources.INSTANCE.css().ensureInjected();
    }

    private Label title;
    private Image buttonSearch;
    private SuggestBox searchBox;

    private Integer value;// TODO change to pk
    private PropertyDTO<Integer> property;
    private ViewDTO view;
    private TableInterface table;
    private DialogBox dialogBox;
    private DockLayoutPanel dialogContents;
    private GlassPanel glassPanel = new GlassPanel();
    private Record selectedRecord = null;
    private boolean dirty;

    public ReferenceFieldWidget(String caption, ViewDTO view, Widget child) {
        super();
        this.view = view;

        // forms container
        dataPanel = new SimplePanel();
        dataPanel.add(child);
        basePanel.add(dataPanel);
        title = new Label(caption);
        navigationPanel.add(title);
        buttonSearch = new Image(RefFormWidgetResources.INSTANCE.find());
        buttonSearch.addClickHandler(new ClickHandler() {
            public void onClick(ClickEvent event) {
                if (dialogBox == null) {
                    dialogBox = createDialogBox();
                }
                glassPanel.show();
                dialogBox.setAnimationEnabled(true);
                dialogContents.setPixelSize(Window.getClientWidth() - 100, Window.getClientHeight() - 100);
                // dialogBox.center();
                // dialogBox.setPopupPosition(Window.getScrollLeft(),
                // Window.getScrollTop());

                dialogBox.show();

                if (ReferenceFieldWidget.this.view.getSortProperty() != null) {
                    ReferenceFieldWidget.this.view.setSearchString(searchBox.getText());
                    table.search();
                } else {
                    table.reload();
                }
            }
        });

        HorizontalFlowPanel searchPanel = new HorizontalFlowPanel();
        if (view.getSortProperty() != null) {
            ItemSuggestOracle oracle = new ItemSuggestOracle();
            searchBox = new SuggestBox(oracle);
            resetSearchBox();
            searchPanel.add(searchBox);
            searchBox.setStyleName(RefFormWidgetResources.INSTANCE.css().refSearchLeft());
            searchBox.getTextBox().addFocusHandler(new FocusHandler() {

                public void onFocus(FocusEvent event) {
                    searchBox.setText("");

                }

            });
            searchBox.addSelectionHandler(new SelectionHandler<Suggestion>() {

                public void onSelection(SelectionEvent<Suggestion> event) {
                    int id = ((ItemSuggestion) event.getSelectedItem()).getId();
                    selectedRecord = ((ItemSuggestion) event.getSelectedItem()).getRecord();
                    setValue(id, true);
                }
            });

        }
        searchPanel.addRight(buttonSearch);
        searchPanel.setHeight("20px");

        buttonSearch.setStyleName(RefFormWidgetResources.INSTANCE.css().refSearchRight());
        searchPanel.setStyleName(RefFormWidgetResources.INSTANCE.css().refSearchAll());

        navigationPanel.addRight(searchPanel);

        navigationPanel.setStyleName(ContainerFieldResources.INSTANCE.css().contentsHeader());
        basePanel.setStyleName(ContainerFieldResources.INSTANCE.css().contentsFrame());
        dataPanel.setStyleName(ContainerFieldResources.INSTANCE.css().contentsHolder());

    }

    private void resetSearchBox() {
        if (view.getSortProperty() != null) {
            searchBox.setText(view.getSortProperty().getLocalizedName() + ":");
        }
    }

    public PropertyDTO<Integer> getProperty() {
        return property;
    }

    public void setProperty(PropertyDTO<Integer> property) {
        this.property = property;

    }

    public void setValue(Integer value) {
        setValue(value, false);
        selectedRecord = null;
        resetSearchBox();
    }

    public void setValue(Integer value, boolean fireEvents) {
        Integer oldValue = this.value;
        this.value = value;
        if (fireEvents) {
            if (value != null && !value.equals(oldValue)){
                setDirty(true);
            }
            ValueChangeEvent.fireIfNotEqual(this, oldValue, value);
        }
    }

    public Integer getValue() {
        return value;
    }

    public HandlerRegistration addValueChangeHandler(ValueChangeHandler<Integer> handler) {
        return this.addHandler(handler, ValueChangeEvent.getType());
    }

    private DialogBox createDialogBox() {
        dialogContents = new DockLayoutPanel(Unit.EM);
        final DialogBox dialogBox = new DialogBox();
        dialogBox.setText("Select reference - " + view.getLocalizedName());
        dialogBox.setWidget(dialogContents);
        HorizontalFlowPanel buttonPanel = new HorizontalFlowPanel();

        Button OKButton = new Button("OK", new ClickHandler() {
            public void onClick(ClickEvent event) {
                dialogBox.hide();
                glassPanel.hide();
                selectedRecord = table.getSelectedRecord();
                setValue(table.getSelectedPrimaryKey().getId(), true);
            }
        });
        buttonPanel.addRight(OKButton);

        Button closeButton = new Button("Cancel", new ClickHandler() {
            public void onClick(ClickEvent event) {
                dialogBox.hide();
                glassPanel.hide();

            }
        });
        buttonPanel.addRight(closeButton);
        dialogContents.addSouth(buttonPanel, 2);

        table = TableFactory.create(view, null, null);
        table.asWidget().setSize("100%", "100%");
        dialogContents.add(table);

        return dialogBox;
    }

    public Record getSelectedRecord() {
        return selectedRecord;
    }

    public void setDirty(boolean dirty) {
        this.dirty = dirty;
        if (dirty) {
            dataPanel.addStyleName(AplikatorResources.INSTANCE.css().dirty());
        } else {
            dataPanel.removeStyleName(AplikatorResources.INSTANCE.css().dirty());
        }
    }

    public boolean isDirty(){
        return dirty;
    }

    @SuppressWarnings("serial")
    public static class ItemSuggestion implements Serializable, Suggestion {

        private String suggestion;
        private int id;
        private String replacement;
        private Record record;

        // Required for IsSerializable to work
        public ItemSuggestion() {
        }

        // Convenience method for creation of a suggestion
        public ItemSuggestion(String suggestion, int id, String replacement, Record record) {
            this.suggestion = suggestion;
            this.id = id;
            this.replacement = replacement;
            this.record = record;
        }

        public String getDisplayString() {
            return suggestion;
        }

        public String getReplacementString() {
            return replacement;
        }

        public int getId() {
            return id;
        }

        public Record getRecord() {
            return record;
        }
    }

    public class ItemSuggestOracle extends SuggestOracle {

        public boolean isDisplayStringHTML() {
            return true;
        }

        public void requestSuggestions(SuggestOracle.Request req, SuggestOracle.Callback callback) {
            Aplikator.aplikatorService.execute(new GetSuggestions(view, req), new ItemSuggestCallback(req, callback));
        }

        class ItemSuggestCallback implements AsyncCallback<GetSuggestionsResponse> {

            private SuggestOracle.Request req;
            private SuggestOracle.Callback callback;

            public ItemSuggestCallback(SuggestOracle.Request r, SuggestOracle.Callback c) {
                req = r;
                callback = c;
            }

            public void onFailure(Throwable error) {
                callback.onSuggestionsReady(req, new SuggestOracle.Response());
            }

            public void onSuccess(GetSuggestionsResponse resp) {
                SuggestOracle.Response retValue = resp.getResponse();
                callback.onSuggestionsReady(req, (SuggestOracle.Response) retValue);
            }
        }
    }

}
